﻿using CQRSFramework.Domains;
using CQRSFramework.Events;
using CQRSFramework.Events.Impl;
using System;
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.Linq;

namespace CQRSFramework.Commands.Impl
{
    public class CommandContext : ICommandContext
    {
        private readonly IEventStore _eventStore;
        private readonly IAggregateFactory _aggregateFactory;
        private ConcurrentDictionary<string, IAggregateRoot> _trackedAggregateRoot = new ConcurrentDictionary<string, IAggregateRoot>();
        public CommandContext(IEventStore repository, IAggregateFactory aggregateFactory)
        {
            _eventStore = repository;
            _aggregateFactory = aggregateFactory;
        }

        public T Get<T>(string aggregateRootId) where T : class, IAggregateRoot
        {
            IAggregateRoot aggregateRoot = null;
            if (_trackedAggregateRoot.TryGetValue(aggregateRootId, out aggregateRoot))
            {
                return aggregateRoot as T;
            }
            var list = _eventStore.Find(aggregateRootId);
            if (list.Any())
            {
                aggregateRoot = _aggregateFactory.CreateAggregate<T>(typeof(T));
                aggregateRoot.LoadFromHistory(list);
                _trackedAggregateRoot.TryAdd(aggregateRootId, aggregateRoot);
                return aggregateRoot as T;
            }
            else
            {
                return default(T);
            }
        }

        public void Add<T>(T aggregateRoot) where T : class, IAggregateRoot
        {
            if (aggregateRoot == null)
            {
                throw new ArgumentNullException("AggregateRoot");
            }
            if (!_trackedAggregateRoot.TryAdd(aggregateRoot.UniqueAggregateRootId, aggregateRoot))
            {
                throw new ArgumentException($"AggregateRoot all ready exists. AggregateRootId:{aggregateRoot.UniqueAggregateRootId}");
            }
        }

        public IEnumerable<IAggregateRoot> GetTrackedAggregateRoots()
        {
            return _trackedAggregateRoot.Values;
        }

        public void Clear()
        {
            _trackedAggregateRoot.Clear();
        }
    }
}
